
/* This is the main driver class for the scheduling simulator.

   This class reads command line args to determine which scheduling algorithm to use, determines 
   the source of the trace file, and runs the simulation.

*/

#include "handles.h"
#include "includes.h"
#include <stdlib.h>
#include "perf_data.h"
#include "init.h"
#include <string.h>
#include "reader.h"
#include "io.h"
#include "stcp.h"
#include "exp.h"
#include "stdio.h"
#include "reader.h"
#define no_events 7                       /* number of event types */

long clock_time;                  /* global clock time        */
int fd;                           /* input fd                 */
struct proc_info *active_proc;    /* points to active process */

int main(int argc, char *argv[]) {
  enum event_type  event_id = -1; /* type of the current event to handle     */

  /* Initialize variables */
  clock_time = 0;
  active_proc = NULL;
  fd = 0;

  /* set up input fd and check CLIs */
  handle_args(argc, argv);

  /* initialize data structures in scheduler and io*/
  scheduler_init();
  io_init();

  /* main loop */
  while (1) {
    /* determine the type of the next event to happen */
    event_id = get_next_event_id();

    /* end simulation if all events have completed */
    if (event_id == END) {
      print_stats();
      exit(0);
    }

    /* otherwise, handle the event */
    handle(event_id);
  }
}

/* Determines the next type of event to happen */
int get_next_event_id() {
  enum event_type event_id = -1;
  long next_event_time = MAXVAL;
  long event_time = MAXVAL;
  int i = 0;

  /* Check all event types next time */
  for (i = 0; i < no_events; i++) {
    switch (i) {
    case NEW_PROCESS:
      /* Check for new a new process arrival */
      event_time = reader_next_event_time();
      break;

    case SCHEDULE:
      /* Check for active process' time slice ending */
      if (active_proc == NULL)
	continue;
      event_time = scheduler_next_event_time();
      break;

    case END_IO:
      /* Check for io completions */
      event_time = io_next_event_time();
      break;

    case KILL_PROCESS:
      /* If a process only has cpu time remaining */
      if (active_proc != NULL) {
	if (active_proc->ios_remaining == 0)
		{
		  event_time = active_proc->time_remaining + clock_time;
		}	
      }
      else
	event_time = MAXVAL;
      break;
      
    case START_IO:
      /* Check if the active process has ios remaining */
      if (active_proc != NULL) {
	if (active_proc->ios_remaining > 0)
	  event_time = active_proc->time_to_next_io + clock_time;
      }
      else 
	event_time = MAXVAL;
      break;
    default:
      event_time = MAXVAL;
    }

    /* Event times of -1 are flags for NO pending events */
    if (event_time != -1 && event_time < next_event_time) {
      next_event_time = event_time;
      event_id = i;
    }
  }
  /* If there are no active processes or events, we may resume a job */
  if (active_proc == NULL && next_event_time > 0 && !scheduler_queue_empty()) {
    next_event_time = clock_time;
    event_id = RESUME_PROCESS;
  }

  /* Check for EOF case */
  if (next_event_time == MAXVAL) {
    return END;
  }
  else {
    /* Advance the clock until the event will happen */
    if (next_event_time >= clock_time) {

      /* If there is an active process, update stats counter */
      if (active_proc != NULL)
	increment_cpu_time(next_event_time - clock_time);

      advance_clock(next_event_time - clock_time);
    }
    return event_id;
  }
}

void handle(enum event_type event_id) {
  switch (event_id) {
  case NEW_PROCESS:
    handle_new_process();
    return;
  case RESUME_PROCESS:
    handle_resume_process();
    return;
  case KILL_PROCESS:
    handle_kill_process();
    return;
  case START_IO:
    handle_start_io();
    return;
  case END_IO:
    handle_end_io();
    return;
  case SCHEDULE:
    handle_schedule();
    return;
  case END:
    printf("Entered unexpected state in event handle: %d", event_id);
    exit(-1);
  default:
    printf("Entered unexpected state in event handler: %d", event_id);
    exit(-1);
  }
}
    
	
void advance_clock(long time_step) {
  scheduler_advance_clock(time_step);
  io_advance_clock(time_step);
  active_process_advance_clock(time_step);
  clock_time += time_step;
}

void active_process_advance_clock(long time_step) {
  if (time_step < 0) {
    perror("Negative time step passed to clock\n");
    exit(-1);
  }

  if (active_proc == NULL) return;

  /* Advance fields for active process */
  active_proc->time_remaining -= time_step;
  active_proc->time_to_next_io -= time_step;
  active_proc->last_runtime += time_step;
}

void handle_args(int argc, char *argv[]) {
  /* Handles command line args and prepares fn pointers */
  if (argc != 2) {
    perror("Usage: schedSim [scheduling algorithm] < inputfile\nScheduling algorithms:\n\texp\n\tstcp\n");
    exit(0);
  }

  if (!strncmp(argv[1], "exp", 4)) {
    /* exponential queue type scheduling */
    scheduler_init = exp_init;
    scheduler_add_to_queue = exp_add_to_queue;
    scheduler_get_next = exp_get_next;
    scheduler_next_event_time = exp_next_event_time;
    scheduler_advance_clock = exp_advance_clock;
    scheduler_queue_empty = exp_queue_empty;
    scheduler_close = exp_close;
  }
  else if (!strncmp(argv[1], "stcp", 4)) {
    /* shortest completion time scheduling */
    scheduler_init = stcp_init;
    scheduler_add_to_queue = stcp_add_to_queue;
    scheduler_get_next = stcp_get_next;
    scheduler_next_event_time = stcp_next_event_time;
    scheduler_advance_clock = stcp_advance_clock;
    scheduler_queue_empty = stcp_queue_empty;
    scheduler_close = stcp_close;
  }
  else {
    perror("Usage: schedSim [scheduling algorithm]\nScheduling algorithms:\n\texp\n\tstcp\n");
    exit(0);
  }
}

